#include "cdevicectrl.h"
#include <QWidget>
#include <QSettings>
#include <QApplication>
#include <QDesktopWidget>

CDeviceCtrl::CDeviceCtrl(QObject *parent) :
    QObject(parent)
{
    this->parent = parent;
    video_buffers = NULL;
    buffer_count = 0;
    pLog = new CDebugLog;
    ReadConfigure("configure.ini");
}

CDeviceCtrl::~CDeviceCtrl()
{
    if(NULL != pSdlModule)
    {
        CloseDevice();
    }
}

int CDeviceCtrl::OpenDevice()
{
    pLog->DebugLog("Open Video Device!");
    //init sdl
    devam=true;
    fd = -1;
    pSdlModule = NULL;
    pSurface = NULL;
    pSdlModule = SdlModuleInit(((QWidget *)(this->parent))->winId(),_DISPLAY_WIDTH, _DISPLAY_HEIGHT,
                               _DISPLAY_WIDTH, _DISPLAY_HEIGHT,
                               audioFrequency, SDL_AUDIO_S16, channel, volume);
    if(NULL == pSdlModule)
    {
        pLog->DebugLog("sdl init failed!");
        return -1;
    }
    pSurface = AvSDLYUVCreate(pSdlModule, 0, 0, _DISPLAY_WIDTH, _DISPLAY_HEIGHT);
    if(NULL == pSurface)
    {
        pLog->DebugLog("Create SDLYUV surface failed!");
        return -1;
    }

    //init video device
    fd = -1;
    fd = v4l2_open(_DEVICE_NAME, O_RDWR | O_NONBLOCK, 0);
    if (fd < 0) {
           pLog->DebugLog("Cannot open device , err:%s", strerror(errno));
           return -1;
    }
    struct v4l2_capability capability;
    CLEAR(capability);
    if(-1 == ioctl(fd, VIDIOC_QUERYCAP, &capability))
    {
        pLog->DebugLog(" VIDIOC_QUERYCAP failed, err:%s", strerror(errno));
        return -1;
    }
    if(0 == (capability.capabilities & V4L2_CAP_VIDEO_CAPTURE))
    {
        pLog->DebugLog("This capture card not suspend video cature, err:%s", strerror(errno));
        return -1;
    }
    struct v4l2_cropcap cropcap;
    struct v4l2_crop crop;
    /* Reset Cropping */
    memset( &cropcap, 0, sizeof(cropcap) );
    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if( v4l2_ioctl( fd, VIDIOC_CROPCAP, &cropcap ) >= 0 )
    {
        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        crop.c = cropcap.defrect; /* reset to default */
        if( crop.c.width > 0 && crop.c.height > 0 ) /* Fix for fm tuners */
        {
            crop.c.top += 1;
            if( v4l2_ioctl( fd, VIDIOC_S_CROP, &crop ) < 0 )
            {
                pLog->DebugLog("This capture card reset crop failed.");
            }
        }
    }


    //set standard std directed
    v4l2_std_id u32VideoStdId = V4L2_STD_PAL;
    if(-1 == ioctl(fd, VIDIOC_S_STD, &u32VideoStdId))
    {
        pLog->DebugLog("VIDIOC_S_STD, err:%s", strerror(errno));
        return -1;
    }

    struct v4l2_input input;
    CLEAR(input);
    input.index = inputIndex;
    if(-1 == ioctl(fd, VIDIOC_ENUMINPUT, &input))
    {
        pLog->DebugLog("VIDIOC_ENUMINPUT, err:%s", strerror(errno));
        return -1;
    }
//    input.std = V4L2_STD_PAL;
    if(-1 == ioctl(fd, VIDIOC_S_INPUT, &input))
    {
        pLog->DebugLog("VIDIOC_S_INPUT, err:%s", strerror(errno));
        return -1;
    }

    struct v4l2_format              fmt;
    CLEAR(fmt);
    ioctl(fd, VIDIOC_G_FMT, &fmt);

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width       = captureWidth;
    fmt.fmt.pix.height      = captureHeight;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
    if(-1 == ioctl(fd, VIDIOC_S_FMT, &fmt))
    {
        pLog->DebugLog("VIDIOC_S_FMT, err:%s", strerror(errno));
        return -1;
    }

    struct v4l2_requestbuffers      req;
    CLEAR(req);
    req.count = 30;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    if (-1 == ioctl(fd, VIDIOC_REQBUFS, &req))
    {
        pLog->DebugLog("VIDIOC_REQBUFS, err:%s", strerror(errno));
        return -1;
    }

    struct v4l2_buffer              buf;
    video_buffers = (VideoBuffer *)calloc(req.count, sizeof(*video_buffers));
    for (buffer_count = 0; buffer_count < req.count; ++buffer_count)
    {
           CLEAR(buf);

           buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
           buf.memory      = V4L2_MEMORY_MMAP;
           buf.index       = buffer_count;

           if(-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
           {
               pLog->DebugLog("VIDIOC_QUERYBUF, err:%s", strerror(errno));
               return -1;
           }

           video_buffers[buffer_count].length = buf.length;
           video_buffers[buffer_count].start = mmap(NULL, buf.length,
                         PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd, buf.m.offset);

           if (MAP_FAILED == video_buffers[buffer_count].start) {
                   pLog->DebugLog("mmap faild, err:%s", strerror(errno));
                   return -1;
           }
           //into the buffer queue
           if(-1 == ioctl(fd, VIDIOC_QBUF, &buf))
           {
               pLog->DebugLog("VIDIOC_QBUF, err:%s", strerror(errno));
               return -1;
           }
    }
    return 0;
}

int CDeviceCtrl::ReadAndDisplay()
{
    pLog->DebugLog("start video and audio thread");
    if(0 != pthread_create(&hThreadDisplayHandle, NULL, ThreadReadAndDispaly, this))
    {
        pLog->DebugLog("start video thread failed.");
        return -1;
    }

    if(0 != pthread_create(&hThreadRecordHandle, NULL, ThreadRecordPcm, GetSdlModule()))
    {
        pLog->DebugLog("start audio thread failed.");
        return -1;
    }

    SDL_PauseAudio(0);
    SDL_ShowCursor(0);
    pLog->DebugLog("start video and audio thread.success");
    return 0;
}

int CDeviceCtrl::CloseDevice()
{
    pLog->DebugLog("Close Device.");
    void *pvReturnValue = NULL;
    //close video
    try{
        devam = false;
        if(0 != pthread_join(hThreadDisplayHandle, &pvReturnValue))
        {
            return 0;
        }
        enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ioctl(fd, VIDIOC_STREAMOFF, &type);
        for (unsigned int i = 0; i < buffer_count; ++i)
            v4l2_munmap(video_buffers[i].start, video_buffers[i].length);

        free(video_buffers);
        v4l2_close(fd);
    }catch(...){}
    if(NULL != pSurface)
    {
        AvSDLYUVDestroy(pSurface);
        pSurface = NULL;
    }
    buffer_count = 0;
    //close audio
    pSdlModule->cap_flag = 0;
    if(0 != pthread_join(hThreadRecordHandle, &pvReturnValue))
    {
        return 0;
    }
    SDL_ShowCursor(1);
    SdlModuleUnInit(pSdlModule);
    pSdlModule = NULL;
    pLog->DebugLog("Close Device.end");
    return 0;
}

int CDeviceCtrl::ReadConfigure(const char *filename)
{
    QSettings setting(filename, QSettings::IniFormat);
//    setting.beginGroup("player");
    inputIndex = setting.value("/player/inputindex").toInt();
    videoFrameRate = setting.value("/player/videoframe").toInt();
    captureWidth = setting.value("/player/sourcewidth").toInt();
    captureHeight = setting.value("/player/sourceheight").toInt();
    isFullScreen = setting.value("/player/fullscreen").toInt()?true:false;
    volume = setting.value("/player/volume").toInt();
    audioFrequency = setting.value("/player/frequency").toInt();
    channel = setting.value("/player/channel").toInt();
    defaultPlayFile = setting.value("/player/defaultfilename").toString();
    pingTimes = setting.value("/ping/count").toInt();
    pingInterval = setting.value("/ping/interval").toInt();
    isMaster = setting.value("/backups/ismaster").toInt()?true:false;
    masterMuticastIp = setting.value("/backups/mastermuticastip").toString();
    backupMuticastIp = setting.value("/backups/backupmuticastip").toString();
    masterEncoderIp = setting.value("backups/masterencoderip").toString();
    masterEncoderPort = setting.value("/backups/masterencoderport").toInt();
    backupEncoderIp = setting.value("/backups/backupencoderip").toString();
    backupEncoderPort = setting.value("/backups/backupencoderport").toInt();
    pingPort = setting.value("/backups/pingport").toInt();
    masterIp = setting.value("/backups/masterip").toString();
    backupIp = setting.value("/backups/backupip").toString();


    pLog->DebugLog("inputIndex=%d;videoFrame=%d;sourceWidth=%d;sourceHeight=%d;\n"
                   "fullscreen=%d;volume=%d;frequency=%d;channel=%d;defaultname=%s;"
                   "pingTimes=%d;pingInterval=%d;isMaster=%d;masterMuticastIp=%s;backupMuticastIp=%s;"
                   "masterEncoderIp=%s;masterEncoderPort=%d; backupEncoderIp=%s; backupEecoderPort=%d;pingport=%d;"
                   "masterip=%s;backupip=%s;",
                   inputIndex, videoFrameRate, captureWidth, captureHeight,
                   isFullScreen?1:0, volume, audioFrequency, channel, defaultPlayFile.toLocal8Bit().data(),
                   pingTimes, pingInterval, isMaster?1:0, masterMuticastIp.toLocal8Bit().data(),
                   backupMuticastIp.toLocal8Bit().data(), masterEncoderIp.toLocal8Bit().data(), masterEncoderPort,
                   backupEncoderIp.toLocal8Bit().data(), backupEncoderPort, pingPort,
                   masterIp.toLocal8Bit().data(), backupIp.toLocal8Bit().data());

    if(0 == volume)
    {
        system("amixer -q set Master mute");
    }else
    {
        system("amixer -q set Master unmute");
        QString str = QString("amixer -q set Master %1").arg(volume);
        system(str.toLocal8Bit().data());
    }

    return 0;
}
